package ace.cmp.spring.data.jdbc.querydsl.core.impl;

import ace.cmp.spring.data.jdbc.querydsl.core.QuerydslQClassContext;
import com.google.common.base.CaseFormat;
import com.querydsl.sql.RelationalPath;
import com.querydsl.sql.RelationalPathBase;
import java.util.concurrent.ConcurrentMap;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ReflectionUtils;

/**
 * @author caspar
 * @date 2023/4/3 17:29
 */
public class QuerydslQClassContextImpl implements QuerydslQClassContext {
  private final ConcurrentMap<Class, RelationalPathBase> pathConcurrentMap =
      new ConcurrentReferenceHashMap<>(10);
  private final NamingStrategy namingStrategy;

  public QuerydslQClassContextImpl(NamingStrategy namingStrategy) {
    this.namingStrategy = namingStrategy;
  }

  @Override
  public <T> RelationalPath<T> getRelationalPathByEntityClass(Class entityClass) {
    RelationalPathBase path = pathConcurrentMap.get(entityClass);

    if (path != null) {
      return path;
    }

    path = this.getRelationalPathBaseFromQueryClass(getQueryClass(entityClass));

    pathConcurrentMap.putIfAbsent(entityClass, path);

    return path;
  }

  private Class<?> getQueryClass(Class<?> entityType) {
    var fullName = entityType.getPackage().getName() + ".Q" + entityType.getSimpleName();
    try {
      return entityType.getClassLoader().loadClass(fullName);
    } catch (ClassNotFoundException e) {
      throw new IllegalArgumentException("Unable to load class " + fullName);
    }
  }

  private RelationalPathBase<?> getRelationalPathBaseFromQueryClass(Class<?> queryClass) {
    var fieldName =
        CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, queryClass.getSimpleName().substring(1));
    var field = ReflectionUtils.findField(queryClass, fieldName);

    if (field == null) {
      throw new IllegalArgumentException(
          "Did not find a static field of the same type in " + queryClass);
    }

    return (RelationalPathBase<?>) ReflectionUtils.getField(field, null);
  }
}
